package com.yu.transport.server;

import com.yu.comcurrent.ThreadPoolFactory;
import com.yu.factory.SingletonFactory;
import com.yu.provider.ServiceProvider;
import com.yu.provider.ServiceProviderImpl;
import com.yu.service.RpcServiceProperties;
import com.yu.transport.handler.NettyServerHandler;
import com.yu.transport.handler.RpcMessageDecoder;
import com.yu.transport.handler.RpcMessageEncoder;
import com.yu.unregrister.CustomShutdownHook;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.util.concurrent.DefaultEventExecutorGroup;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.net.InetAddress;
import java.util.concurrent.TimeUnit;

/**
 * @PackageName: com.yu.transport.server
 * @Description:
 * @Version: v1.0.0
 * @Author: yuweilin
 * @Date: 10:27
 * <p>
 * Modification History:
 * Date         Author          Version            Description
 * ------------------------------------------------------------
 * 2021/1/27      yuweilin         v1.0.0               新建
 */
@Slf4j
@Component
public class NettyServer {
    /**
     * 服务器的开放端口
     * 我们在将服务注册到zookeeper的时候，使用的就是这个地址
     */
    public static final int PORT=9998;

    private final ServiceProvider serviceProvider;

    public NettyServer() {
        serviceProvider = SingletonFactory.getInstance(ServiceProviderImpl.class);
    }

    public void registerService(Object service, RpcServiceProperties rpcServiceProperties){
        serviceProvider.publishService(service,rpcServiceProperties);
    }

    @SneakyThrows
    public void start(){
        NioEventLoopGroup bossGroup = new NioEventLoopGroup(1);
        NioEventLoopGroup workGroup = new NioEventLoopGroup();
        DefaultEventExecutorGroup serviceHandlerGroup = new DefaultEventExecutorGroup(
                Runtime.getRuntime().availableProcessors()*2,
                ThreadPoolFactory.createThreadFactory("server-handler-group",false)
        );
        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(bossGroup,workGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
                    .childOption(ChannelOption.SO_KEEPALIVE,true)
                    .childOption(ChannelOption.TCP_NODELAY,true)
                    .option(ChannelOption.SO_BACKLOG,128)
                    .handler(new LoggingHandler(LogLevel.INFO))
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ChannelPipeline pipeline = ch.pipeline();
                            pipeline
                                    .addLast(new IdleStateHandler(20,0,0, TimeUnit.SECONDS))
                                    .addLast(new RpcMessageDecoder())
                                    .addLast(new RpcMessageEncoder())
                                    .addLast(serviceHandlerGroup,new NettyServerHandler());
                        }
                    });
            //监听端口，同步等待绑定成功
            ChannelFuture future = bootstrap.bind(PORT).sync();
            //等待监听端口关闭
            future.channel().closeFuture().sync();
        }catch (InterruptedException e){
            log.error("服务器启动发生异常",e);
        }finally {
            log.info("关闭netty服务端资源");
            bossGroup.shutdownGracefully();
            workGroup.shutdownGracefully();
            serviceHandlerGroup.shutdownGracefully();
            CustomShutdownHook.getCustomShutdownHook().clearAll();
        }
    }


}
